using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;

namespace GameLib
{
    public class RoundCappedLine : IGeometrySegment
    {
        LineSegment lineSegment1;
        ArcSegment arcSegment1;
        LineSegment lineSegment2;
        ArcSegment arcSegment2;
        public Vector2 Point1 { private set; get; }
        public Vector2 Point2 { private set; get; }
        public float Radius { private set; get; }

        public RoundCappedLine(Vector2 point1, Vector2 point2, float radius)
        {
            Point1 = point1;
            Point2 = point2;
            Radius = radius;
            Vector2 vector = point2 - point1;
            Vector2 normVect = vector;
            normVect.Normalize();
            Vector2 pt1a = Point1 + radius * new Vector2(normVect.Y, -normVect.X);
            Vector2 pt2a = pt1a + vector;
            Vector2 pt1b = Point1 + radius * new Vector2(-normVect.Y, normVect.X);
            Vector2 pt2b = pt1b + vector;
            lineSegment1 = new LineSegment(pt1a, pt2a);
            arcSegment1 = new ArcSegment(point2, radius, pt2a, pt2b);
            lineSegment2 = new LineSegment(pt2b, pt1b);
            arcSegment2 = new ArcSegment(point1, radius, pt1b, pt1a);
        }

        public void GetAllX(float y, IList<float> xCollection)
        {
            arcSegment1.GetAllX(y, xCollection);
            lineSegment1.GetAllX(y, xCollection);
            arcSegment2.GetAllX(y, xCollection);
            lineSegment2.GetAllX(y, xCollection);
        }
    }

    public interface IGeometrySegment
    {
        void GetAllX(float y, IList<float> xCollection);
    }

    public struct LineSegment : IGeometrySegment
    {
        readonly float a, b;         // as in x = ay + b
        public Vector2 Point1 { private set; get; }
        public Vector2 Point2 { private set; get; }

        public LineSegment(Vector2 point1, Vector2 point2)
            : this()
        {
            Point1 = point1;
            Point2 = point2;
            a = (Point2.X - Point1.X) / (Point2.Y - Point1.Y);
            b = Point1.X - a * Point1.Y;
        }


        public void GetAllX(float y, IList<float> xCollection)
        {
            if ((Point2.Y > Point1.Y && y >= Point1.Y && y < Point2.Y) ||
                (Point2.Y < Point1.Y && y <= Point1.Y && y > Point2.Y))
            {
                xCollection.Add(a * y + b);
            }
        }
    }

    public struct ArcSegment : IGeometrySegment
    {
        readonly double angle1, angle2;
        public Vector2 Center { private set; get; }
        public float Radius { private set; get; }
        public Vector2 Point1 { private set; get; }
        public Vector2 Point2 { private set; get; }

        public ArcSegment(Vector2 center, float radius,
                          Vector2 point1, Vector2 point2)
            : this()
        {
            Center = center;
            Radius = radius;
            Point1 = point1;
            Point2 = point2;
            angle1 = Math.Atan2(point1.Y - center.Y, point1.X - center.X);
            angle2 = Math.Atan2(point2.Y - center.Y, point2.X - center.X);
        }

        public void GetAllX(float y, IList<float> xCollection)
        {
            double sqrtArg = Radius * Radius - Math.Pow(y - Center.Y, 2);
            if (sqrtArg >= 0)
            {
                double sqrt = Math.Sqrt(sqrtArg);
                TryY(y, Center.X + sqrt, xCollection);
                TryY(y, Center.X - sqrt, xCollection);
            }
        }

        public void TryY(double y, double x, IList<float> xCollection)
        {
            double angle = Math.Atan2(y - Center.Y, x - Center.X);
            if ((angle1 < angle2 && (angle1 <= angle && angle < angle2)) ||
                (angle1 > angle2 && (angle1 <= angle || angle < angle2)))
            {
                xCollection.Add((float)x);
            }
        }
    }
}
